﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using TagLib;
using TagCore;
using AmazonQuery;
using GoogleQuery;
using FreeDBQuery;

namespace TagManager
{
    public partial class Form1 : Form, IUserInterface
    {
        #region Data
        private IUserInterface ui;
        private IMusicQuery query;
        private IMusicTag tag;
        private SearchControl searchControl;
        private ListViewItem activeLvi = null;
        private string access; //amazon
        private string key;//amazon
        #endregion

        public Form1(string access, string key)
        {
            InitializeComponent();

            this.access = access;
            this.ui = this;// new DummyUI();
            this.key = key;

            ConfigurePictureBox(pictureBox1);
            DisplaySearchControl();
            LoadGenres();
            LoadPatterns();
            LoadProviders();
        }

        private void LoadPatterns()
        {
            cbFilePattern.Items.AddRange(FilePattern.GetPatterns());
            cbFilePattern.SelectedIndex = 0;
        }

        #region Private Methods
        private void LoadProviders()
        {
            AddProvider(new TagLib.TagLibMusic());
            AddProvider(new DummyTag());
            cbTagProvider.SelectedIndex = 0;

            AddProvider(new AmazonSearch(access, key, "https://webservices.amazon.com/onca/soap?Service=AWSECommerceService"));
            AddProvider(new GoogleSearch());
            AddProvider(new FreedbSearch());
            cbSearchProvider.SelectedIndex = 0;
        }

        private void AddProvider(IMusicQuery query)
        {
            cbSearchProvider.Items.Add(new ComboItem(query.Provider(), query));
        }

        private void AddProvider(IMusicTag tag)
        {
            cbTagProvider.Items.Add(new ComboItem(tag.ToString(), tag));
        }

        private void LoadGenres()
        {
            cbGenre.Items.AddRange(TagCore.Genres.Get());
        }

        private void DisplayFiles(string folder)
        {
            txtFolder.Text = folder;
            lvFileView.Clear();
            ConfigureListView();

            // Get all the files
            FileData[] files = ui.GetFiles(folder, tag.SupportedTypes());
            foreach (FileData fd in files)
            {
                ListViewItem lvi = new ListViewItem(fd.Filename);
                lvi.Tag = fd;
                lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, fd.LastModified.ToString("g")));

                lvFileView.Items.Add(lvi);
            }
        }

        private void ConfigureListView()
        {
            lvFileView.HeaderStyle = ColumnHeaderStyle.Clickable;
            lvFileView.View = View.Details;
            lvFileView.MultiSelect = false;

            ColumnHeader ch = new ColumnHeader();
            ch.Text = "Filename";
            ch.Width = 220;
            lvFileView.Columns.Add(ch);

            ch = new ColumnHeader();
            ch.Text = "Modified";
            ch.Width = 110;
            lvFileView.Columns.Add(ch);
            lvFileView.ColumnClick += new ColumnClickEventHandler(lvFileView_ColumnClick);
        }

        void lvFileView_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            lvFileView.ListViewItemSorter = new ListViewItemComparer(e.Column);
        }

        private void ConfigurePictureBox(PictureBox pb)
        {
            pb.SizeMode = PictureBoxSizeMode.Zoom;
            PictureUpdated = false;
            pb.DoubleClick += new EventHandler(pb_DoubleClick);
            pb.MouseUp += new MouseEventHandler(pb_MouseUp);
        }

        void pb_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                if (pictureBox1.Image != null)
                    Clipboard.SetImage(pictureBox1.Image);
            }
        }

        void pb_DoubleClick(object sender, EventArgs e)
        {
            if (Clipboard.ContainsImage())
                SetPicture(Clipboard.GetImage());
        }

        private void UpdateVariableTagData(IMusicData md, QueryFields type)
        {
            if ((type & QueryFields.Title) == QueryFields.Title)
                txtTitle.Text = md.Title;
            if ((type & QueryFields.Image) == QueryFields.Image)
                SetPicture(md.Image);
            if ((type & QueryFields.Artist) == QueryFields.Artist)
                txtArtist.Text = TagCoreUtil.CombineStrings(md.Artists);
            if ((type & QueryFields.AlbumArtist) == QueryFields.AlbumArtist)
                txtAlbumArtist.Text = TagCoreUtil.CombineStrings(md.AlbumArtists);
            if ((type & QueryFields.Year) == QueryFields.Year)
                txtYear.Text = md.Year.ToString();
            if ((type & QueryFields.Track) == QueryFields.Track)
                txtTrack.Text = md.Track.ToString();
            if ((type & QueryFields.Album) == QueryFields.Album)
                txtAlbum.Text = md.Album;
            if ((type & QueryFields.Genre) == QueryFields.Genre)
                cbGenre.Text = md.Genre;
        }

        private void UpdateStaticTagData(IMusicData md)
        {
            txtComment.Text = md.Comments;
        }

        private void UpdateFileData(FileData fd)
        {
            txtFilename.Text = fd.Filename;
        }

        private void UpdateSearch()
        {
            ClearResults();
            // update search control
            searchControl.Update(tag.Data);
        }

        private void Rename()
        {
            tag.Close();
            ActiveFile.Rename(txtFilename.Text);
            LoadFile();
        }

        private FileData ActiveFile
        {
            get
            {
                if (activeLvi != null)
                    return (FileData)activeLvi.Tag;

                return null;
            }
        }

        private void UpdateListView()
        {
            activeLvi.SubItems[0].Text = ActiveFile.Filename;
            activeLvi.SubItems[1].Text = ActiveFile.LastModified.ToString("g");
        }

        private void SetPicture(byte[] image)
        {
            if (image != null && image.Length > 0)
                SetPicture(Image.FromStream(new MemoryStream(image)));
            else
                pictureBox1.Image = null;
        }

        private void SetPicture(Image image)
        {
            byte[] pic = ResizeImage(image, 400);
            pictureBox1.Image = Image.FromStream(new MemoryStream(pic));
        }

        public static byte[] ResizeImage(Image imgPhoto, int targetSize)
        {
            int targetH = targetSize;
            int targetW = targetSize;
            // Create a new blank canvas.  The resized image will be drawn on this canvas.
            Bitmap bmPhoto = new Bitmap(targetW, targetH, PixelFormat.Format24bppRgb);
            bmPhoto.SetResolution(72, 72);
            Graphics grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.SmoothingMode = SmoothingMode.AntiAlias;
            grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
            grPhoto.PixelOffsetMode = PixelOffsetMode.HighQuality;
            grPhoto.DrawImage(imgPhoto, new Rectangle(0, 0, targetW, targetH), 0, 0, imgPhoto.Width, imgPhoto.Height, GraphicsUnit.Pixel);
            // Save out to memory and then to a file.  We dispose of all objects to make sure the files don't stay locked.
            MemoryStream mm = new MemoryStream();
            bmPhoto.Save(mm, System.Drawing.Imaging.ImageFormat.Jpeg);
            imgPhoto.Dispose();
            bmPhoto.Dispose();
            grPhoto.Dispose();
            return mm.GetBuffer();
        }

        private void ClearResults()
        {
            // Remove search tabs
            for (int loop = tabControl1.TabPages.Count - 1; loop > 0; loop--)
                tabControl1.TabPages.RemoveAt(loop);
        }

        private void DisplaySearchControl()
        {
            searchControl = new SearchControl();
            searchControl.ViewResults += new ViewResultsHandler(searchControl_ViewResults);
            TabPage tp = new TabPage("Search");
            tp.Controls.Add(searchControl);

            tabControl1.TabPages.Add(tp);
        }

        public string GetFolder()
        {
            return "c:\\users\\sykes\\music\\library\\singles";
            // Get file types
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            fbd.ShowNewFolderButton = false;
            DialogResult dr = fbd.ShowDialog();
            if (dr == System.Windows.Forms.DialogResult.OK)
                return fbd.SelectedPath;

            return null;
        }

        public FileData[] GetFiles(string folder, TagCore.FileTypes types)
        {
            List<FileData> files = new List<FileData>();
            foreach (TagCore.FileTypes ft in Enum.GetValues(typeof(TagCore.FileTypes)))
            {
                if (types.HasFlag(ft))
                {
                    string search = "*." + ft;
                    string[] fs = Directory.GetFiles(folder, search);

                    foreach (string file in fs)
                        files.Add(new FileData(file));
                }
            }
            return files.ToArray();
        }

        private void LoadFile()
        {
            tag.Load(ActiveFile);
            UpdateVariableTagData(tag.Data, QueryFields.All);
            UpdateStaticTagData(tag.Data);
            UpdateFileData(ActiveFile);
            UpdateSearch();
            PictureUpdated = false;
        }

        private bool PictureUpdated
        {
            get
            {
                return (bool)pictureBox1.Tag;
            }
            set
            {
                pictureBox1.Tag = value;
            }
        }

        private bool Dirty(out bool rename)
        {
            List<bool> flags = new List<bool>();
            flags.Add(string.Compare(tag.Data.Title, txtTitle.Text) != 0);
            flags.Add(string.Compare(tag.Data.Album, txtAlbum.Text) != 0);
            flags.Add(string.Compare(TagCoreUtil.CombineStrings(tag.Data.AlbumArtists), txtAlbumArtist.Text) != 0);
            flags.Add(string.Compare(TagCoreUtil.CombineStrings(tag.Data.Artists), txtArtist.Text) != 0);
            flags.Add(string.Compare(tag.Data.Genre, cbGenre.Text) != 0);
            flags.Add(string.Compare(tag.Data.Comments, txtComment.Text) != 0);
            flags.Add(tag.Data.Track != Convert.ToUInt32(txtTrack.Text));
            flags.Add(tag.Data.Year != Convert.ToUInt32(txtYear.Text));
            flags.Add((rename = string.Compare(txtFilename.Text, ActiveFile.Filename) != 0));
            flags.Add(PictureUpdated);

            foreach (bool f in flags)
                if (f) { return true; }

            return false;
        }

        private byte[] BytesFromImage(Image image)
        {
            MemoryStream ms = new MemoryStream();
            image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            return ms.ToArray();
        }

        private void SaveTagData()
        {
            tag.Data.Title = txtTitle.Text;
            tag.Data.Album = txtAlbum.Text;
            tag.Data.AlbumArtists = TagCoreUtil.SplitStrings(txtAlbumArtist.Text);
            tag.Data.Artists = TagCoreUtil.SplitStrings(txtArtist.Text);
            tag.Data.Genre = cbGenre.Text;
            tag.Data.Comments = txtComment.Text;
            tag.Data.Track = Convert.ToUInt32(txtTrack.Text);
            tag.Data.Year = Convert.ToUInt32(txtYear.Text);
            tag.Data.Image = BytesFromImage(pictureBox1.Image);
            tag.Save();
        }
        #endregion

        #region Private Handlers
        private void searchControl_ViewResults(object sender, ResultsEventArgs e)
        {
            ClearResults();
            for (int loop = 0; loop < e.Results.Length; loop++)
            {
                ResultControl rc = new ResultControl();
                rc.ResultData += new ResultDataHandler(rc_ResultData);

                TabPage tp = new TabPage("Result " + (loop + 1));
                tp.Controls.Add(rc);
                tabControl1.TabPages.Add(tp);

                rc.Display(e.Results[loop]);

                tabControl1.SelectedIndex = 1;
            }
        }

        private void rc_ResultData(object sender, ResultDataEventArgs e)
        {
            UpdateVariableTagData(e.Data, e.Type);
            PictureUpdated = (e.Type == QueryFields.Image);
        }

        private void cbTagProvider_SelectedIndexChanged(object sender, EventArgs e)
        {
            tag = ((ComboItem)cbTagProvider.SelectedItem).Value as IMusicTag;
        }

        private void cbSearchProvider_SelectedIndexChanged(object sender, EventArgs e)
        {
            query = ((ComboItem)cbSearchProvider.SelectedItem).Value as IMusicQuery;
            tabControl1.SelectedIndex = 0;
            searchControl.SetQuery(query);
        }

        private void pbBrowse_Click(object sender, EventArgs e)
        {
            string folder = ui.GetFolder();
            if (!string.IsNullOrEmpty(folder))
                DisplayFiles(folder);
        }

        private void lvFileView_DoubleClick(object sender, System.EventArgs e)
        {
            ListViewItem lvi = lvFileView.SelectedItems[0];

            // check if changed
            if (lvi != activeLvi)
            {
                // save
                activeLvi = lvi;
            }

            // set new file
            LoadFile();
        }

        private void pbSave_Click(object sender, EventArgs e)
        {
            bool rename;
            if (Dirty(out rename))
            {
                SaveTagData();
                if (rename)
                    Rename();
                UpdateListView();
            }
        }

        private void pbApply_Click(object sender, EventArgs e)
        {
            txtFilename.Text = FilePattern.GetFileName(cbFilePattern.Text, tag.Data);
        }

        private void pbRefresh_Click(object sender, EventArgs e)
        {
            tag.Close();
            LoadFile();
        }
        #endregion
    }

    // Implements the manual sorting of items by columns.
    public class ListViewItemComparer : IComparer
    {
        private int col;
        public ListViewItemComparer()
        {
            col = 0;
        }
        public ListViewItemComparer(int column)
        {
            col = column;
        }
        public int Compare(object x, object y)
        {
            if (col == 0)
                return String.Compare(((ListViewItem)x).SubItems[col].Text, ((ListViewItem)y).SubItems[col].Text);
            else
                return DateTime.Compare(DateTime.Parse(((ListViewItem)x).SubItems[col].Text), DateTime.Parse(((ListViewItem)y).SubItems[col].Text));
        }
    }

    public class ComboItem : object
	{
		protected string m_Name;
		protected object m_Value;
		
		public ComboItem(string name, object in_value)
		{
			m_Name	= name;
			m_Value	= in_value;
		}

        public object Value
        {
            get
            {
                return m_Value;
            }
        }

        public override string ToString()
		{
			return m_Name;
		}
	};
}
